/***********************************************************************************
Copy right:	    Coffee Tech.
Author:         wusong
Date:           2021-11-21
Description:    设备采集服务
***********************************************************************************/

#include <fcntl.h> /*文件控制定义*/
#include <stdio.h> /*标准输入输出定义*/
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>	 /*Unix 标准函数定义*/

#include "edge_pub.h"
#include "shmem.h"
#include "log_utils.h"
#include "nv_utils.h"

#include "m0_uart.h"
#include "dc_service.h"

#define MODULE DC_NAME

/****************************************************/
static struct shm_param para;			   //共享内存
struct node_cache data_nodes;			   //数据点
short node_key_map[MAX_DATA_NODE];  //数据点key映射表
struct m0_node_list m0_list;			   //m0数据点链表
char dc_unix_buf[MAX_UNIX_LEN];				//用于接收unix,控制json的缓冲区
struct unix_param dc_unix_ser;

/****************************/



/**
 * @brief 获取edge 初始化的std_node  shm
 *
 * @return int
 */

int shm_get(void)
{
    int ret = -1;

    ret = shm_init(&para, EDGE_SHM_NAME, MAX_NODE_SIZE);
    if (ret < 0)
    {
        return -1;
    }

    void *node_p = shm_getaddr(&para);
    if (node_p == NULL)
    {
        return -1;
    }

    data_nodes.total = (int *)node_p;
    data_nodes.node_p = (struct std_node *)(node_p + sizeof(int));

    for (int i = 0; i < 6; i++)
    {
        LOGD("node %d - key %d", i, data_nodes.node_p[i].key);
    }
    return 0;
}
int node_count = 0;

/**
 * @brief 解析m0的数据点
 *
 * @return int 解析结果, 成功 0, 失败 -1
 */
int m0_nodes_init(void)
{

    //解析m0的数据点
    int arr_size = -1;

    struct nv_obj *pobj;

    //解析点表
    struct list_head nv_list;

    nv_get_list_from_file(&nv_list, NODE_CONFIG_PAHT);

    pobj = nv_get_list_obj(&nv_list, "m0.data.arr_size");
    if (NULL == pobj)
    {
        LOG_STD(APP_ERR, "get err");
    }
    arr_size = pobj->valueint;
    LOG_STD(APP_DEBUG, "m0 arr size = %d", arr_size);

    struct m0_node_list *nodep;				 //当前节点

    INIT_LIST_HEAD(&m0_list.list);
    for (int i = 0; i < arr_size; i++)
    {
        char key_str[256] = {0};
        char type_str[256] = {0};
        char name_str[256] = {0};

        snprintf(key_str, 255, "m0.data[%d].key", i);
        snprintf(type_str, 255, "m0.data[%d].type", i);
        snprintf(name_str, 255, "m0.data[%d].name", i);

        //申请节点内存
        nodep = (struct m0_node_list *)malloc(sizeof(*nodep));
        bzero(nodep, sizeof(*nodep));

        pobj = nv_get_list_obj(&nv_list, key_str);
        nodep->node.key = pobj->valueint;

        pobj = nv_get_list_obj(&nv_list, type_str);
        nodep->node.type = pobj->valueint;
        //get node name
        pobj = nv_get_list_obj(&nv_list, name_str);
        strcpy(nodep->node.name, pobj->valuestring);

        list_add_tail(&nodep->list, &m0_list.list);

        LOGD("node %s, key %d", nodep->node.name, nodep->node.key)
                node_count++;
    }


    nv_list_free(&nv_list);

    return 0;
}

/**
 * @brief 映射数据点id
 *
 */
void m0_data_id_map(void)
{

    // for (struct list_head *node = m0_list.list.next;
    // 	 node; node = node->next)
    struct list_head *node;
    list_for_each(node, &m0_list.list)
    {
        struct m0_node *m0_data_node = &((struct m0_node_list *)node)->node;
        LOGI("ndoe name : %s", m0_data_node->name);
        char *nodename = m0_data_node->name;

        if (!strcmp(nodename, NAME_STR_TEMPERATURE))
        {
            //温度
            node_key_map[DATA_TYPE_ID_TEMPERATURE] = m0_data_node->key;
        }

        if (!strcmp(nodename, NAME_STR_HUMIDITY))
        {
            //湿度
            node_key_map[DATA_TYPE_ID_HUMIDITY] = m0_data_node->key;
        }

        if (!strcmp(nodename, NAME_STR_ILL))
        {
            //亮度
            node_key_map[DATA_TYPE_ID_ILL] = m0_data_node->key;
        }

        if (!strcmp(nodename, NAME_STR_LIGHT))
        {
            //灯
            node_key_map[DATA_TYPE_ID_LIGHT] = m0_data_node->key;
        }

        if (!strcmp(nodename, NAME_STR_ALARM))
        {
            //报警
            node_key_map[DATA_TYPE_ID_ALARM] = m0_data_node->key;
        }

        if (!strcmp(nodename, NAME_STR_FAN))
        {
            //风扇
            node_key_map[DATA_TYPE_ID_FAN] = m0_data_node->key;
        }
    }
}




/**
 * @brief 初始化服务
 * @return 0 -1
 */
int init_dc_service()
{
    //初始化unix_sock的服务端
    if(unix_init_server(&dc_unix_ser, DC_NAME) < 0)
    {
        printf("error to unix_init_server\n");
        return -1;
    }


    //获取点表
    if (shm_get() < 0)
    {
        LOGE("can not get shm %s", para.name);
        exit(-1);
    }

    m0_nodes_init();

    m0_data_id_map();

    m0_init();

    return 0;
}

/**
 * @brief 根据key获取数据点node
 *
 * @param key
 * @return struct std_node* 共享内存node指针
 */
struct std_node *node_get(int key)
{
    // LOGI("node count %d", node_count);
    for (int i = 0; i < node_count; i++)
    {
        // LOGI("node key %d, %d", data_nodes.node_p[i].key, key);
        if (data_nodes.node_p[i].key == key)
        {
            return &data_nodes.node_p[i];
        }
    }

    return NULL;
}

inline void node_set_val(struct std_node *node, double data)
{
    LOGD("data : %f", data)
            switch (node->type)
    {
        case 1:	 //bool
            node->new_val.b_val = data;
            break;
        case 2:	 //int
            node->new_val.i_val = data;
            break;
        case 3:	 //float
            node->new_val.f_val = data;
            break;
        default:
            break;
    }
}



/**
 * @brief Get the node val
 *
 * @param key
 * @param val 用于返回
 * @return int 状态 0 -1
 */
int get_node_val(int key, int *val)
{
    struct list_head *node;
    struct m0_node *m0_node;
    list_for_each(node, &m0_list.list)
    {
        m0_node = &((struct m0_node_list *)node)->node;
        if (m0_node->key == key)
        {
            *val = m0_node->val.i_val;
            LOGI("get m0 node %d %d", key, *val);
            return 0;
        }
    }

    return -1;
}


void m0_set_fan_led_alarm_to_shm(void)
{
    struct std_node *node = node_get(node_key_map[DATA_TYPE_ID_FAN]);
    int val = 0;
    get_node_val(node->key, &val);
    node_set_val(node, val);

    node = node_get(node_key_map[DATA_TYPE_ID_ALARM]);
    get_node_val(node->key, &val);
    node_set_val(node, val);

    node = node_get(node_key_map[DATA_TYPE_ID_LIGHT]);
    get_node_val(node->key, &val);
    node_set_val(node, val);
}

/**
 * @brief 将收集到的数据写到 data_report开辟的共享内存
 *
 */
void m0_set_data_to_shm(void)
{
    m0_env_info_t *data = (m0_env_info_t *)uart_r_buf;

#ifdef DEBUG
    LOGD("head:%s(%#02x %#02x %#02x)\n", data->head, data->head[0], data->head[1], data->head[2]);
    LOGD("type:%#02x\n", data->type);
    LOGD("snum:%#02x\n", data->snum);
    LOGD("temp:%#02x %#02x\n", data->temp[0], data->temp[1]);
    LOGD("humi:%#02x %#02x\n", data->hum[0], data->hum[1]);
    LOGD("xyx :%#02x %#02x %#02x\n", data->x, data->y, data->z);
    LOGD("ill :%#08x\n", data->ill);
    LOGD("bet :%#08x\n", data->bet);
    LOGD("adc :%#08x\n", data->adc);
#endif

    //设置温度
    struct std_node *node = node_get(node_key_map[DATA_TYPE_ID_TEMPERATURE]);

    if (node != NULL)
    {
        LOGI("set temperature %d", data->temp[0]);
        node_set_val(node, data->temp[0]);
    }
    else
    {
        LOGE("temp node null");
    }

    //设置湿度
    node = node_get(node_key_map[DATA_TYPE_ID_HUMIDITY]);
    if (node != NULL)
    {
        LOGI("set humidity %f", data->hum[0]);
        node_set_val(node, data->hum[0]);
    }
    else
    {
        LOGE("hum node null");
    }

    //设置光亮度
    node = node_get(node_key_map[DATA_TYPE_ID_ILL]);
    if (node != NULL)
    {
        LOGI("set ill %d", data->ill);
        node_set_val(node, data->ill);
    }
    else
    {
        LOGE("ill node null");
    }

    m0_set_fan_led_alarm_to_shm();
}


/**
 * @brief 采集m0的数据，并将最新的数据写到共享内存响应的节点中
 * @param arg
 * @return
 */
void *m0_collect_thread(void *arg)
{

    int ret = -1;

    while (1)
    {
        LOGD("collect ...");
        m0_uart_get_header(uart_r_buf);

        /****************************************/
        //收数据
        ret = m0_read_with_len_block(uart_r_buf + header_total, sizeof(m0_env_info_t) - header_total);
        if (ret <= 0)
        {
            LOGE("recv data from m0 error, it will be reopen soon");
            m0_uart_reopen();
            continue;
        }

        /****************************************/
        //解析数据,传到report service
        m0_set_data_to_shm();

        sleep(1);
    }

    return NULL;
}


#define CMD_RESULT_SUCC		"{\"type\": 2,\"result\": 0,\"msg\":\"控制成功\"}"
#define CMD_RESULT_FAIL		"{\"type\": 2,\"result\": -1,\"msg\":\"%s\"}"


int cmd_get_kv(char *buf, int *key, int *val)
{

    struct list_head nv_list;
    struct nv_obj *pobj;

    if (nv_get_list_from_string(&nv_list, buf) < 0)
    {
        LOGE("dc parse cmd fail %s", buf);
        //目前先粗暴的用进程退出来解决
        exit(-1);
    }
    pobj = nv_get_list_obj(&nv_list, "type");

    if (!pobj)
    {
        LOGE("dc cmd get type fail %s", buf);
        sleep(1);
        return -1;
    }

    if (pobj->valueint != 2)
    {
        LOGE("cmd type not match %s", buf);
        sleep(1);
        return -1;
    }


    /***************************************************/
    //解析key
    pobj = nv_get_list_obj(&nv_list, "data.key");

    if (!pobj)
    {
        LOGE("dc cmd get key fail %s", buf);
        sleep(1);
        return -1;
    }

    *key = pobj->valueint;
    /**************************************************/
    //解析val
    pobj = nv_get_list_obj(&nv_list, "data.val");

    if (!pobj)
    {
        LOGE("dc cmd get val fail %s", buf);
        sleep(1);
        return -1;
    }
    *val = atoi(pobj->valuestring);

    return 0;
}


void get_result_str(int state, char *dc_unix_buf)
{
    bzero(dc_unix_buf, MAX_UNIX_LEN);
    if (state < 0)
    {
        LOGE("send cmd to m0 fail");
        sprintf(dc_unix_buf, CMD_RESULT_FAIL, "send to m0 fail");
    }
    else
    {
        sprintf(dc_unix_buf, CMD_RESULT_SUCC);

    }
}


void set_node_cache(int key, int val)
{
    struct list_head *node;
    struct m0_node *m0_node;
    list_for_each(node, &m0_list.list)
    {
        m0_node = &((struct m0_node_list *)node)->node;
        if (m0_node->key == key)
        {
            m0_node->val.i_val = val;
            LOGI("set m0 node %d %d", key, val);
            return;
        }
    }
}


/**
 * @brief 接收来自上层进程的指令，解析并处理
 * @param arg
 * @return
 */
void *msg_handle_thread(void *arg)
{

    int key = 0;
    int val = 0;
    int ret = -1;

    while (1)
    {

        bzero(dc_unix_buf, MAX_UNIX_LEN);

        LOGN("handle cmd");

        if (unix_server_recv(&dc_unix_ser, dc_unix_buf, MAX_UNIX_LEN) < 0)
        {
            LOGE("dc unix recv fail");
            //目前先粗暴的用进程退出来解决
            exit(-1);
        }

        LOGN("recv msg=%s", dc_unix_buf);

        if (cmd_get_kv(dc_unix_buf, &key, &val) < 0)
        {
            LOGE("parse json fail");
            continue;
        }

        ret = m0_set_cmd(key, val);

        get_result_str(ret, dc_unix_buf);

        set_node_cache(key, val);

        ret = unix_server_send(&dc_unix_ser, dc_unix_buf, strlen(dc_unix_buf));
        if (ret < 0)
        {
            LOGE("send result to cmd sender fail %s", strerror(errno));
        }

        sleep(1);
    }

    return NULL;
}
